
#define LOG_TAG "GlTest"

#include "FrameQueue.h"

namespace android {


FrameQueue::FrameQueue(int w, int h, int bpp) : mWidth(w), mHeight(h), mBPP(bpp) {
    int ret = initBufferSlot();

    if (ret < 0) {
        ALOGE("%s: initBufferSlot failed!", __func__);
    }
}

FrameQueue::~FrameQueue() {
    freeBufferSlot();
}

//status_t FrameQueue::setFrameFormat(int w, int h, int bpp) {
//    mWidth = w; 
//    mHeight = h;
//    mBPP = bpp;
//
//    return 0;
//}

status_t FrameQueue::initBufferSlot() {
    //std::lock_guard<std::mutex> l(mMutex);
    Mutex::Autolock _l(mMutex);

    int size = mWidth * mHeight * mBPP / 8;
    int cnt = NUM_BUFFER_SLOTS;

    mPtr = (void *)malloc(size * cnt);
    if (mPtr == NULL) {
        mPtr = NULL;
        return -ENOMEM;
    }

    for (int i = 0; i < cnt; i++) {
        mSlots[i].mSlotId = i;
        mSlots[i].mBufferPtr = (uint8_t *)mPtr + size * i;
        mSlots[i].mBufferState = FREE;

        mFreeQueue.push(mSlots[i]);
        ALOGD("%s: i=%d, slotId=%d, bufferPtr=%p", __func__, i, mSlots[i].mSlotId, mSlots[i].mBufferPtr);
    }

    return 0;
}

status_t FrameQueue::freeBufferSlot() {
    //std::lock_guard<std::mutex> l(mMutex);
    Mutex::Autolock _l(mMutex);

    while (!mFreeQueue.empty()) mFreeQueue.pop();
    while (!mReadyQueue.empty()) mReadyQueue.pop();

    int cnt = NUM_BUFFER_SLOTS;

    for (int i = 0; i < cnt; i++) {
        mSlots[i].mSlotId = -1;
        mSlots[i].mBufferPtr = NULL;
        mSlots[i].mBufferState = INVALID;

        ALOGD("%s: i=%d, slotId=%d, bufferPtr=%p", __func__, i, mSlots[i].mSlotId, mSlots[i].mBufferPtr);
    }

    if (mPtr != NULL) {
        free(mPtr);
        mPtr = NULL;
    }

    return 0;
}

//BufferSlot& getBufferSlotLocked(int idx) {
//    return mSlots[idx];
//}

status_t FrameQueue::dequeueBuffer(int* buf, void** ptr) {
    //std::lock_guard<std::mutex> l(mMutex);
    Mutex::Autolock _l(mMutex);

    if (mFreeQueue.empty())
        return -EBUSY;

    BufferSlot& slot = mFreeQueue.front();

    slot.mBufferState = DEQUEUED;
    *buf = slot.mSlotId;
    *ptr = slot.mBufferPtr;

    mFreeQueue.pop();

    ALOGD("%s: slotId=%d, bufferPtr=%p", __func__, slot.mSlotId, slot.mBufferPtr);
    return 0;
}

status_t FrameQueue::queueBuffer(int idx) {
    if (idx < 0 || idx > NUM_BUFFER_SLOTS)
        return -EINVAL;

    {
        //std::lock_guard<std::mutex> l(mMutex);
        Mutex::Autolock _l(mMutex);
    
        //auto slot = getBufferSlotLocked(buf);
        auto slot = mSlots[idx];
        slot.mBufferState = QUEUED;

        mReadyQueue.push(slot);

        ALOGD("%s: slotId=%d, bufferPtr=%p", __func__, slot.mSlotId, slot.mBufferPtr);
    }
    //mCond.notify_all();
    mCond.broadcast();

    return 0;
}

status_t FrameQueue::acquireBuffer(int *idx, void** ptr) {
    //std::unique_lock<std::mutex> l(mMutex);
    mMutex.lock();

    while (mReadyQueue.empty()) {
        //mCond.wait(l);
        mCond.wait(mMutex);
    }

    BufferSlot& slot = mReadyQueue.front();
    slot.mBufferState = ACQUIRED;
    *idx = slot.mSlotId;
    *ptr = (void *)slot.mBufferPtr;
    mReadyQueue.pop();

    mMutex.unlock();
    ALOGD("%s: slotId=%d, bufferPtr=%p", __func__, slot.mSlotId, slot.mBufferPtr);
    return 0;
}

status_t FrameQueue::releaseBuffer(int idx) {
    if (idx < 0 || idx > NUM_BUFFER_SLOTS)
        return -EINVAL;

    {
        //std::lock_guard<std::mutex> l(mMutex);
        Mutex::Autolock _l(mMutex);
    
        //auto slot = getBufferSlotLocked(idx);
        auto slot = mSlots[idx];
        slot.mBufferState = FREE;
        mFreeQueue.push(slot);

        ALOGD("%s: slotId=%d, bufferPtr=%p", __func__, slot.mSlotId, slot.mBufferPtr);
    }

    return 0;
}


}   // namespace
